#pragma once
#include "util.hpp"
#include <list>
#include <condition_variable>
#include <thread>

template <class T>
class match_queue
{
private:
    std::list<T> _list; // 这里我们不适用队列是因为有删除指定元素的需求
    std::mutex _mutex;
    std::condition_variable _con; // 条件变量，出队时使用当数据<2时,就阻塞
public:
    int size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }
    void push(const T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _con.notify_all();
    }
    // 阻塞线程
    void Wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _con.wait(lock);
    }
    bool pop(T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_list.empty())
            return false;
        data = _list.front();
        _list.pop_front();
        return true;
    }
    // 删除队列中的指定元素
    void remove(T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }
};

class matcher
{
private:
    match_queue<uint64_t> _q_normal; // 普通水平的
    match_queue<uint64_t> _q_high;   // 高水平的
    match_queue<uint64_t> _q_super;  // 超高水平的
    std::thread _th_normal;
    std::thread _th_high;
    std::thread _th_super;
    user_table *_ut;  // 用来查看数据库，以便添加用户和删除用户
    online_user *_ou; // 查看用户是否在游戏大厅
    room_manager *_rm; // 创建游戏房间
private:
    void handler_task(match_queue<uint64_t> &mq)
    {
        while (true)
        {
            // 1.队列中人数<2就等待
            while (mq.size() < 2)
            {
                mq.Wait();
            }
            // 2.出队，如果有一方出队失败，那么另外一方要放入队列中
            uint64_t uid1, uid2;
            bool ret = mq.pop(uid1);
            if (ret == false)
                continue;
            ret = mq.pop(uid2);
            if (ret == false)
            {
                mq.push(uid1);
                continue;
            }
            // 3.查看这两个用户是否都在线，他们一定在游戏大厅中等待，如果其中一个不在线，就把另外一个放入队列中
            wsserver_t::connection_ptr wc1 = _ou->get_conn_from_hall(uid1);
            if(wc1.get() == nullptr)
            {
                mq.push(uid2);
                continue;
            }
            wsserver_t::connection_ptr wc2 = _ou->get_conn_from_hall(uid2);
            if(wc2.get() == nullptr)
            {
                mq.push(uid1);
                continue;
            }
            // 4.为他们创建游戏房间，如果创建失败，把他们都放入队列中
            room_ptr rp = _rm->room_create(uid1,uid2);
            if(rp.get() == nullptr)
            {
                mq.push(uid1);
                mq.push(uid2);
                continue;
            }
            // 5.给用户进行应答
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string body;
            json_util::serialize(resp,body);
            wc1->send(body);
            wc2->send(body);
        }
    }

public:
    matcher(user_table* ut,online_user* ou,room_manager* rm)
    :_ut(ut),_ou(ou),_rm(rm),_th_normal(std::thread(&matcher::thread_normal_enter,this)),
    _th_high(std::thread(&matcher::thread_high_enter,this)),_th_super(std::thread(&matcher::thread_super_enter,this))
    {
        DLOG("matcher 创建完成!");
    }
    // 设置3个线程执行函数
    void thread_normal_enter() { return handler_task(_q_normal); }
    void thread_high_enter() { return handler_task(_q_high); }
    void thread_super_enter() { return handler_task(_q_super); }
    // 给用户添加到匹配队列中
    bool add(uint64_t uid)
    {
        // 查看用户信息是否在数据库中
        Json::Value root;
        bool ret = _ut->select_by_id(uid, root);
        if (ret == false)
        {
            DLOG("没有该用户%d 的信息", uid);
            return false;
        }
        // 根据用户的分数来确定匹配队列
        int score = root["score"].asInt();
        if (score < 2000)
            _q_normal.push(uid);
        else if (score < 3000)
            _q_high.push(uid);
        else
            _q_super.push(uid);
        return true;
    }
    // 将用户从队列中删除
    bool del(uint64_t uid)
    {
        // 查看用户信息是否在数据库中
        Json::Value root;
        bool ret = _ut->select_by_id(uid, root);
        if (ret == false)
        {
            DLOG("没有该用户%d 的信息", uid);
            return false;
        }
        // 根据用户的分数来确定匹配队列
        int score = root["score"].asInt();
        if (score < 2000)
            _q_normal.remove(uid);
        else if (score < 3000)
            _q_high.remove(uid);
        else
            _q_super.remove(uid);
        return true;
    }
};